﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.IO;

using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System.Web;

namespace SPHelper
{
	public class File
	{

		/// <summary>
		/// Get a SPFile from the passed url
		/// </summary>
		/// <param name="web"></param>
		/// <param name="absoluteUrl"></param>
		/// <returns></returns>
		public static SPFile GetFileByUrl(SPWeb web, string url)
		{
			SPFile file = null;
			try
			{
				// init
				if (web == null) { web = Web.GetWeb(); }
				if (web == null) { throw new Exception("SPWeb passed is null or can't get from SPContext"); }

				// get file
				file = web.GetFile(url);
			}
			catch (Exception ex)
			{
				Log.WriteError("e57jiyGGdfesw6rugf", ex, "url: " + url);
				file = null;
			}
			return file;
		}

		public static SPFile GetFileByUrl(string url)
		{
			return GetFileByUrl(null, url);
		}

		/// <summary>
		/// Get file, does recursive lookup of all folders in document library
		/// </summary>
		/// <param name="list"></param>
		/// <param name="filename"></param>
		/// <returns></returns>
		public static SPFile GetFileByFilename(SPList list, string filename)
		{
			SPFile file = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPlist passed is null"); }
				if (string.IsNullOrEmpty(filename)) { throw new Exception("Filename not defined"); }

				// find file in root folder
				file = GetFile(list.RootFolder, filename, null, null);
			}
			catch (Exception ex)
			{
				Log.WriteError("e57jiytddfesw6rugf", ex);
				file = null;
			}
			return file;
		}

		/// <summary>
		/// file an spfile, recursive
		/// Choose only one of the parameters to search against
		/// </summary>
		/// <param name="folder"></param>
		/// <param name="filename">leave null if not searching by filename</param>
		/// <param name="documentTitle">leave null if not searching by title</param>
		/// <param name="uniqueId">leave null if not searching by uniqueId</param>
		/// <returns></returns>
		public static SPFile GetFile(SPFolder folder, string filename, string documentTitle, Guid? uniqueId)
		{
			SPFile file = null;

			// look in current folder for file
			if (folder != null && folder.Files != null && folder.Files.Count > 0)
			{
				foreach (SPFile f in folder.Files)
				{
					if (!string.IsNullOrEmpty(filename) && string.Compare(f.Name, filename, true) == 0) 
					{ 
						file = f; break; 
					}
					else if (!string.IsNullOrEmpty(documentTitle) && string.Compare(f.Title, documentTitle) == 0) 
					{ 
						file = f; break; 
					}
					else if (uniqueId != null && f.UniqueId == uniqueId.Value) 
					{ 
						file = f; break; 
					}
				}
			}

			// look in sub folders if not found
			if (file == null && folder.SubFolders != null && folder.SubFolders.Count > 0)
			{
				foreach (SPFolder f in folder.SubFolders)
				{
					file = GetFile(f, filename, documentTitle, uniqueId);
					if (file != null) { break; }
				}
			}
			return file;
		}

		/// <summary>
		/// Get a folder by passed url
		/// </summary>
		/// <param name="web"></param>
		/// <param name="url"></param>
		/// <returns></returns>
		public static SPFolder GetFolderByUrl(SPWeb web, string url)
		{
			SPFolder folder = null;
			try
			{
				// init
				if (!Validation.IsValidUrl(url)) { throw new Exception("Invalid URL passed"); }
				if (web == null) { web = Web.GetWeb(); }
				if (web == null) { throw new Exception("SPWeb passed is null or can't get from SPContext"); }


				folder = web.GetFolder(url);
			}
			catch (Exception ex)
			{
				Log.WriteError("tytiutytHrdfgcf", ex, "url: " + url);
				folder = null;
			}
			return folder;
		}

		public static SPFolder GetFolderByUrl(string url)
		{
			return GetFolderByUrl(null, url);
		}

		/// <summary>
		/// Get folder by passing folder Guid
		/// </summary>
		/// <param name="web"></param>
		/// <param name="folderId"></param>
		/// <returns></returns>
		public static SPFolder GetFolderById(SPWeb web, Guid folderId)
		{
			SPFolder folder = null;
			try
			{
				// init
				if (folderId == null || folderId == Guid.Empty) { throw new Exception("FolderId passed is null"); }
				if (web == null) { web = Web.GetWeb(); }
				if (web == null) { throw new Exception("SPWeb passed is null or can't get from SPContext"); }


				folder = web.GetFolder(folderId);
			}
			catch (Exception ex)
			{
				Log.WriteError("tytiuRtrdshdfdfgcf", ex, "folderId: " + folderId);
				folder = null;
			}
			return folder;
		}

		public static SPFolder GetFolderById(Guid folderId)
		{
			return GetFolderById(null, folderId);
		}


		

		/// <summary>
		/// Determin if OpenXML SDK Installed
		/// This is required for manipulating Office documents
		/// </summary>
		/// <returns></returns>
		public static bool IsOpenXmlSdkInstalled()
		{
			try
			{
				string assemblyName = "DocumentFormat.OpenXml, Version=2.0.5022.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35";
				System.Reflection.Assembly assembly = System.Reflection.Assembly.Load(assemblyName);
				return (assembly != null);
			}
			catch
			{
				return false;
			}
		}

		/// <summary>
		/// Create a new Word document with passed string
		/// Then saves new document to specified SPList
		/// </summary>
		/// <param name="folderPath"></param>
		/// <param name="filename"></param>
		/// <param name="list"></param>
		/// <param name="contents"></param>
		/// <param name="isOverwriteIfFileAlreadyExists"></param>
		/// <returns></returns>
		public static SPFile CreateWordFile(SPList list, string folderPath, string filename, string documentTitle,
			string contents, bool isOverwriteIfFileAlreadyExists)
		{
			SPFile file = null;
			try
			{
				// init
				if (string.IsNullOrEmpty(filename)) { throw new Exception("Filename not defined"); }
				if (list == null) { throw new Exception("SPList passed is null"); }

				// update filename
				if (filename.Contains(".doc") && filename.Contains(".docx")) { filename += ".doc"; }

				// create binary stream
				MemoryStream documentStream = new MemoryStream();
				WordprocessingDocument wordDocument = WordprocessingDocument.Create(documentStream, WordprocessingDocumentType.Document);
				MainDocumentPart mainPart = wordDocument.AddMainDocumentPart();

				// Create the document structure and add some text.
				// http://msdn.microsoft.com/en-us/library/ff478190.aspx
				mainPart.Document = new Document(
					new Body(
						new Paragraph(
							new Run(
								new Text(contents)))));

				wordDocument.Close();

				// add to document library
				file = AddFile(list, folderPath, filename, documentTitle, documentStream, isOverwriteIfFileAlreadyExists);
				if (file == null || !file.Exists) { throw new Exception("Failed to create file"); }
			}
			catch (Exception ex)
			{
				Log.WriteError("stfrjdytksr64e5", ex, "folderPath: " + folderPath + " filename: " + filename + " contents: " + contents);
				file = null;
			}
			return file;
		}

		/// <summary>
		/// Create a plain text file
		/// </summary>
		/// <param name="list"></param>
		/// <param name="folderPath"></param>
		/// <param name="filename"></param>
		/// <param name="documentTitle"></param>
		/// <param name="contents"></param>
		/// <param name="isOverwriteIfFileAlreadyExists"></param>
		/// <returns></returns>
		public static SPFile CreateTextFile(SPList list, string folderPath, string filename, string documentTitle,
			string contents, SPUser createdBy, bool isOverwriteIfFileAlreadyExists)
		{
			SPFile file = null;
			try
			{
				// init
				if (string.IsNullOrEmpty(filename)) { throw new Exception("Filename not defined"); }
				if (list == null) { throw new Exception("SPList passed is null"); }

				// create binary stream
				MemoryStream documentStream = new MemoryStream();
				StreamWriter writer = new StreamWriter(documentStream);
				writer.Write(contents);
				writer.Flush();

				// add to document library
				if (createdBy == null)
				{
					// anon user adding file, use alternat add member
					file = AddFile(list, folderPath, filename, documentTitle, documentStream, isOverwriteIfFileAlreadyExists);
				}
				else
				{
					file = AddFile(list, folderPath, filename, documentTitle, documentStream, null, createdBy, createdBy,
						null, null, null, isOverwriteIfFileAlreadyExists);
				}
				if (file == null || !file.Exists) { throw new Exception("Failed to create file"); }
			}
			catch (Exception ex)
			{
				Log.WriteError("sreydtj6554easdagas3t", ex);
				file = null;
			}
			return file;
		}







		///// <summary>
		///// Add files to a folder
		///// </summary>
		///// <param name="folder"></param>
		///// <param name="filesToAdd"></param>
		///// <param name="createdBy"></param>
		///// <param name="createdDate"></param>
		///// <param name="isOverwrite"></param>
		///// <param name="isRemoveFilesFailedToAdd"></param>
		///// <param name="filesFailedToAdd"></param>
		//public static void AddManyDeprecated(SPFolder folder, List<FileProperties> filesToAdd, SPUser createdBy, DateTime? createdDate,
		//    bool isOverwrite, bool isRemoveFilesFailedToAdd,
		//    ref List<string> filesFailedToAdd)
		//{
		//    string currentFilename = null;
		//    try
		//    {
		//        // init
		//        if (folder == null) { throw new Exception("SPFolder passed is null"); }
		//        if (filesToAdd == null) { throw new Exception("Passed an empty collection of SPFile"); }
		//        SPFileCollection files = folder.Files;
		//        if (files == null) { throw new Exception("Failed to get SPFileColelction from SPFolder passed"); }
		//        if (createdBy == null) { createdBy = Security.GetUser(); }
		//        if (createdDate == null || createdDate == DateTime.MinValue) { createdDate = DateTime.Now; }


		//        if (filesToAdd.Count > 0)
		//        {
		//            foreach (FileProperties file in filesToAdd)
		//            {
		//                currentFilename = file.Filename;
		//                try
		//                {
		//                    Hashtable h = file.GetProperties();
		//                    files.Add(folder.Url + "/" + h["Name"], file.Binary, h, createdBy, createdBy,
		//                        (DateTime)createdDate, (DateTime)createdDate, isOverwrite);
		//                }
		//                catch (Exception ex)
		//                {
		//                    filesFailedToAdd.Add(currentFilename);
		//                    Log.WriteError("rthdfdgdfr5d", "Failed to add file: " + currentFilename + ". Error: " + ex.Message + " \n\n\n" + ex.ToString());
		//                }
		//            }

		//            // remove documents that failed to save
		//            if (isRemoveFilesFailedToAdd && filesFailedToAdd != null && filesFailedToAdd.Count > 0)
		//            {
		//                for (int i = 0; i < filesToAdd.Count; i++)
		//                {
		//                    bool isMatch = false;
		//                    foreach (string filename in filesFailedToAdd)
		//                    {
		//                        if (filesToAdd[i].Filename == filename)
		//                        {
		//                            isMatch = true;
		//                            break;
		//                        }
		//                    }
		//                    if (isMatch)
		//                    {
		//                        filesToAdd.RemoveAt(i);
		//                        i--;
		//                    }
		//                }
		//            } // remove items that failed to save
		//        }
		//    }
		//    catch (Exception ex)
		//    {
		//        SPHelper.Log.WriteError("rdtudtffddsr5r", ex, "current file: " + currentFilename);
		//    }
		//}

		public enum FileProperty
		{
			vti_author,
			vti_cachedtitle,
			vti_metatags,
			vti_progid,
			vti_cachedbodystyle,
			vti_modifiedby,
			vti_charset,
			vti_sourcecontrolcheckincomment,
			vti_nexttolasttimemodified,
			vti_filesize,
			vti_docstoretype,
			vti_language,
			vti_generator,
			vti_timelastmodified,
			vti_title,
			vti_docstoreversion,
			vti_sourcecontrolcookie,
			vti_sourcecontrolversion,
			vti_cachedneedsrewrite,
			vti_timecreated,
			vti_cachedcustomprops,
			vti_cachedhastheme
		}
		

		/// <summary>
		/// Takes passed memory stream and adds to document library
		/// Will automatically reformat passed filename into a valid filename (illegal chars replaced, truncated if too long)
		/// </summary>
		/// <param name="list">document library to add file to</param>
		/// <param name="folderPath">Folder path file should be added to (excludes libray name and filename)</param>
		/// <param name="filename">Required field. If null, will throw error</param>
		/// <param name="documentTitle">If null will use filename</param>
		/// <param name="documentStream"></param>
		/// <param name="properties">metadata properties SharePoint stores for SPFiles</param>
		/// <param name="createdBy"></param>
		/// <param name="modifiedBy"></param>
		/// <param name="timeCreated">if null, uses current dateTime</param>
		/// <param name="timeLastModified">if null, uses current dateTime</param>
		/// <param name="approvalComment"></param>
		/// <param name="isOverwriteIfFileAlreadyExists"></param>
		/// <returns>If SPFile returned, then add is successful</returns>
		public static SPFile AddFile(SPList list, string folderPath, string filename, string documentTitle,
			Stream documentStream, Hashtable properties, SPUser createdBy, SPUser modifiedBy,
			DateTime? timeCreated, DateTime? timeLastModified, string approvalComment, 
			bool isOverwriteIfFileAlreadyExists)
		{
			string originalFilename = filename;
			string folderPlusFilePath = string.Empty;
			SPFile file = null;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (string.IsNullOrEmpty(filename)) { throw new Exception("Filename passed is null"); }
				if (documentStream == null || documentStream == MemoryStream.Null) { throw new Exception("Memory stream passed is null"); }

				// defaults
				if (createdBy == null) { createdBy = Security.GetUser(); }
				if (modifiedBy == null) { modifiedBy = Security.GetUser(); }
				if (createdBy == null && modifiedBy != null) { createdBy = modifiedBy; }
				if (modifiedBy == null && createdBy != null) { modifiedBy = createdBy; }
				DateTime now = DateTime.Now;
				if (timeCreated == null || timeCreated.Value == DateTime.MinValue) { timeCreated = now; }
				if (timeLastModified == null || timeLastModified.Value == DateTime.MinValue) { timeLastModified = now; }
				if (string.IsNullOrEmpty(documentTitle)) { documentTitle = filename; }
				string checkInComment = null;
				string listUrl = SPHelper.List.GetUrl(list);
				int numberOfCharInUrlAlready = 0; 
				if (!string.IsNullOrEmpty(listUrl)) { numberOfCharInUrlAlready += listUrl.Length; }
				if (!string.IsNullOrEmpty(folderPath)) { numberOfCharInUrlAlready += folderPath.Length; }

				// format filename, ensure valid name
				filename = FormatFilename(filename, numberOfCharInUrlAlready, false, false, true);
				if (string.IsNullOrEmpty(filename)) { throw new Exception("Failed to format valid filename"); }

				// concatenate folder path + filename
				folderPlusFilePath = concatenateFolderAndFilename(List.GetUrl(list), folderPath, filename);

				// update properties hash
				if (properties == null) { properties = new Hashtable(); }
				bool isTitlePropertyDefined = false;
				bool isDateCreatedPropertyDefined = false;
				bool isDateModifiedPropertyDefined = false;
				byte numberKeysFound = 0;
				foreach (DictionaryEntry item in properties)
				{
					if (item.Key.ToString().ToLower() == FileProperty.vti_title.ToString()) { isTitlePropertyDefined = true; numberKeysFound++; }
					if (item.Key.ToString().ToLower() == FileProperty.vti_timecreated.ToString()) { isDateCreatedPropertyDefined = true; numberKeysFound++; }
					if (item.Key.ToString().ToLower() == FileProperty.vti_timelastmodified.ToString()) { isDateModifiedPropertyDefined = true; numberKeysFound++; }
					// found them all!
					if (numberKeysFound == 3) { break; }
				}
				// set default properties hashtable
				if (!isTitlePropertyDefined) { properties.Add(FileProperty.vti_title.ToString(), documentTitle); }
				if (!isDateCreatedPropertyDefined) { properties.Add(FileProperty.vti_timecreated.ToString(), timeCreated); }
				if (!isDateModifiedPropertyDefined) { properties.Add(FileProperty.vti_timelastmodified.ToString(), timeLastModified); }

				
				// add to doc lib, return SPFile
				bool isUpdate = false;
				if (createdBy != null && modifiedBy != null)
				{
					file = list.RootFolder.Files.Add(folderPlusFilePath, documentStream, properties,
						createdBy, modifiedBy, (DateTime)timeCreated, (DateTime)timeLastModified, checkInComment,
						isOverwriteIfFileAlreadyExists);
				}
				else
				{
					// created by and modified by SPUser not defined and current user not logged in (i.e.,  anon)
					// http://frazzleddad.blogspot.com/2011/03/api-overloads-dont-overload-different.html
					file = list.RootFolder.Files.Add(folderPlusFilePath, documentStream, properties, 
						isOverwriteIfFileAlreadyExists);

					// check in
					if (List.IsCheckInOutRequired(list)) { file.CheckIn(checkInComment); isUpdate = true; }
				}
				if (file == null || !file.Exists) { throw new Exception("Failed to add file"); }

				// approve
				if (List.IsApprovalRequired(list))
				{
					file.Approve(approvalComment);
					isUpdate = true;
				}

				// update file
				if (isUpdate) { file.Update(); }
			}
			catch (Exception ex)
			{
				Log.WriteError("erydeir6fdregd6", ex, "Folder Path: " + folderPath 
					+ "\r\nOriginal Filename: " + originalFilename 
					+ "\r\nFilename: " + filename 
					+ "\r\nFolder + File Path: " + folderPlusFilePath);
				file = null;
			}
			return file;
		}



		public static SPFile AddFile(SPList list, string folderPath, string filename, byte[] documentByteArray,
			bool isOverwriteIfFileAlreadyExists)
		{
			string folderPlusFilePath = string.Empty;
			SPFile file = null;
			try
			{
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (documentByteArray == null || documentByteArray.Length == 0) { throw new Exception("Byte array is null"); }

				// concatenate folder path + filename
				folderPlusFilePath = concatenateFolderAndFilename(list.Title, folderPath, filename);

				// add to doc lib, return SPFile
				file = list.RootFolder.Files.Add(folderPlusFilePath, documentByteArray, isOverwriteIfFileAlreadyExists);
				if (file == null || !file.Exists) { throw new Exception("Failed to add file"); }
			}
			catch (Exception ex)
			{
				Log.WriteError("7idtyserhgse55yh", ex, "folderpath: " + folderPath + " filename: " + filename + " folderPlusFilePath: " + folderPlusFilePath);
				file = null;
			}
			return file;
		}

		/// <summary>
		/// Add a new document to Document Library
		/// </summary>
		/// <param name="list">the existing document library to add doc to</param>
		/// <param name="folderPath">folder w/in the doc library (Null if adding to root of library)</param>
		/// <param name="filename">filename for the document</param>
		/// <param name="documentTitle">display title for the document</param>
		/// <param name="documentStream">memory stream of the binary file</param>
		/// <param name="isOverwriteIfFileAlreadyExists"></param>
		/// <returns></returns>
		public static SPFile AddFile(SPList list, string folderPath, string filename, string documentTitle,
			MemoryStream documentStream, bool isOverwriteIfFileAlreadyExists)
		{
			return AddFile(list, folderPath, filename, documentTitle, documentStream,
				null, null, null, null, null, null, isOverwriteIfFileAlreadyExists);
		}

		/// <summary>
		/// concatenate folder path + filename, useful for referencing SPFiles in doc lib
		/// </summary>
		/// <param name="folderPath"></param>
		/// <param name="filename"></param>
		/// <returns></returns>
		private static string concatenateFolderAndFilename(string documentLibraryUrl, string folderPath, string filename)
		{
			string o = null;

			// document library title
			if (!string.IsNullOrEmpty(documentLibraryUrl))
			{
				// remove trailing "/" from doc lib path
				if (documentLibraryUrl.Length > 1 && documentLibraryUrl.Substring(documentLibraryUrl.Length - 2, 1) == "/")
				{
					documentLibraryUrl = documentLibraryUrl.Substring(0, documentLibraryUrl.Length - 1);
				}
			}
			else
			{
				documentLibraryUrl = string.Empty;
			}

			// folderPath
			if (!string.IsNullOrEmpty(folderPath))
			{
				// trim
				folderPath = folderPath.Trim();

				// remove preceeding "/" from folder path
				if (folderPath.Length > 0 && folderPath.Substring(0, 1) == "/")
				{
					folderPath = folderPath.Substring(1, folderPath.Length - 1);
				}

				// remove trailing "/" from folder path
				if (folderPath.Length > 1 && folderPath.Substring(folderPath.Length - 2, 1) == "/")
				{
					folderPath = folderPath.Substring(0, folderPath.Length - 1);
				}
			}
			else
			{
				folderPath = string.Empty;
			}


			// filename
			if (!string.IsNullOrEmpty(filename))
			{
				// trim
				filename = filename.Trim();

				// add preceeding "/" from filename
				if (filename.Length > 0 && filename.Substring(0, 1) == "/")
				{
					filename = filename.Substring(1, filename.Length - 1);
				}

				// remove trailing "/" from filename
				if (filename.Length > 1 && filename.Substring(filename.Length - 2, 1) == "/")
				{
					filename = filename.Substring(0, filename.Length - 1);
				}
			}
			else
			{
				filename = string.Empty;
			}

			// concatenate
			if (!string.IsNullOrEmpty(documentLibraryUrl)) { o += documentLibraryUrl + "/"; }
			if (!string.IsNullOrEmpty(folderPath)) { o += folderPath + "/"; }
			if (!string.IsNullOrEmpty(filename)) { o += filename; }

			// remove trailing slash if not root
			if (o != null && o.Length > 1 && o.Substring(o.Length-1) == "/")
			{
				o = o.Substring(0, o.Length - 1);
			}

			return o;
		}

		/// <summary>
		/// Replace keyword with value for passed stream
		/// Dictionary passed must have keyword to find and keyword to replace.
		/// keyword to find is CASE SENSITIVE!
		/// </summary>
		/// <param name="documentStream">file to replace text</param>
		/// <param name="keywordsToReplace">dictionary of keyword to find and value to replace it with. (Case sensative) 
		/// Recommend keywords have unique chars to help identify, such as "[[Keyword]]"</param>
		/// <param name="isThrowErrorIfKeywordDoesntExist"></param>
		/// <returns></returns>
		public static MemoryStream ReplaceKeywords(MemoryStream documentStream, Dictionary<string, string> keywordsToReplace,
			bool isThrowErrorIfKeywordDoesntExist)
		{
			string currentKeyword = string.Empty;
			try
			{
				// init
				if (documentStream == null || documentStream == MemoryStream.Null) { throw new Exception("Memory string passed is null"); }
				if (keywordsToReplace == null || keywordsToReplace.Count == 0) { throw new Exception("Keywords to replace dictionary passed is null"); }

				// read stream, replace keywords
				string documentText = string.Empty;
				using (StreamReader sr = new StreamReader(documentStream))
				{
					documentText = sr.ReadToEnd();
				}
				if (string.IsNullOrEmpty(documentText)) { throw new Exception("Failed to convert document stream into text via stream reader"); }

				foreach (KeyValuePair<string, string> keywordToReplace in keywordsToReplace)
				{
					currentKeyword = keywordToReplace.Key;
					if (isThrowErrorIfKeywordDoesntExist && !documentText.Contains(keywordToReplace.Key))
					{
						throw new Exception("Document passed does not have keyword: " + keywordToReplace.Key);
					}
					documentText = documentText.Replace(keywordToReplace.Key, keywordToReplace.Value);
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("rtsh7e5e6rsdfs4tdr", ex, "Failed on Keyword: " + currentKeyword);
				return null;
			}
			return documentStream;
		}

		/// <summary>
		/// Update only the binary file attached to an SPFile object
		/// </summary>
		/// <param name="file"></param>
		/// <param name="documentStream"></param>
		/// <param name="properties">http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfilesavebinaryparameters_members.aspx</param>
		/// <returns></returns>
		public static bool UpdateFileBinaryOnly(SPWeb web, SPFile file, MemoryStream documentStream, SPFileSaveBinaryParameters properties,
			bool isCheckOutThenCheckBackIn, string checkInComment, bool isDisableOfficePropertyPromotion
			)
		{
			bool isSuccessful = false;
			try
			{
				// init
				if (file == null) { throw new Exception("SPFile passed is null"); }
				if (documentStream == null || documentStream == MemoryStream.Null) { throw new Exception("Memory string passed is null"); }

				// is disble property promotion
				// http://platinumdogs.wordpress.com/2010/03/29/sharepoint-updating-office-based-document-library-items-automatic-document-property-promotion/
				if (isDisableOfficePropertyPromotion)
				{
					if (web == null) { throw new Exception("SPWeb passed was null. This cannot be null when Office Property promotion is disabled"); }
					web.ParserEnabled = false;
					web.Update();
				}

				// checkout?
				if (isCheckOutThenCheckBackIn) { file.CheckOut(); }

				// Update Binary
				file.SaveBinary(documentStream, properties);

				if (isDisableOfficePropertyPromotion)
				{
					// re-enable Office Property promotions
					web.ParserEnabled = true;
					web.Update();
				}

				// checkin?
				if (isCheckOutThenCheckBackIn) { file.CheckIn(checkInComment); }

				// assume success
				isSuccessful = true;
			}
			catch (Exception ex)
			{
				Log.WriteError("rdtufy8uktdhrdsrtsdrt", ex);
				isSuccessful = false;
			}
			return isSuccessful;
		}

		///// <summary>
		///// Update file properties, then save
		///// </summary>
		///// <param name="file">the file to update</param>
		///// <param name="filename">the new filename</param>
		///// <param name="documentTitle">the new title</param>
		///// <param name="description">the new description. To remove existing description, pass string.empty</param>
		///// <param name="createdBy"></param>
		///// <param name="modifiedBy"></param>
		///// <param name="createdDate"></param>
		///// <param name="modifiedDate"></param>
		///// <param name="otherFileProperties">other file properties that are encoded into the binary ("vti_" properties)</param>
		///// <param name="otherListItemProperties">other list item properties: SPFile.Item.Properties</param>
		///// <returns>Return the updated SPFile</returns>
		//public static SPFile UpdateFileProperties(SPFile file, string filename, string documentTitle, string description,
		//    SPUser createdBy, SPUser modifiedBy, DateTime? createdDate, DateTime? modifiedDate,
		//    Hashtable otherFileProperties, Hashtable otherListItemProperties)
		//{
		//    try
		//    {
		//        // IMPORTANT: 
		//        // SPFile.Properties are the properties in the Office document binary and takes a while to parse
		//        // SPFile.Item.Properties are properties for the SPListItem, pulled from SQL and faster to access.
		//        // http://stackoverflow.com/questions/1388502/when-is-spfile-properties-to-spfile-item-properties-in-sharepoint


		//        throw new Exception("This member is not working yet");

		//        // init
		//        bool isUpdateFileProperties = false;
		//        if (file == null) { throw new Exception("SPFile passed is null"); }
		//        if (file.Item == null) { throw new Exception("SPFile has a null item"); }
		//        if (file.Item.Properties == null) { throw new Exception("SPFile.Item.Properties is null"); }


		//        // filename
		//        if (!string.IsNullOrEmpty(filename)) { file.Item.Properties["name"] = filename; }

		//        // document title
		//        if (!string.IsNullOrEmpty(documentTitle))
		//        {
		//            file.Item.Properties["title"] = documentTitle;
		//        }

		//        // description
		//        if (description != null) { file.Item.Properties["description"] = description; }

		//        // created by
		//        if (createdBy != null)
		//        {
		//            file.Item.Properties["Created By"] = createdBy;
		//            file.Item.Properties["Modified By"] = file.Item.Properties["Modified By"];
		//        }

		//        // modified by
		//        if (modifiedBy != null)
		//        {
		//            file.Item.Properties["Modified By"] = modifiedBy;
		//            file.Item.Properties["Created By"] = file.Item.Properties["Created By"];
		//        }

		//        // create date
		//        if (createdDate != null)
		//        {
		//            file.Item.Properties["Created"] = createdDate;
		//            file.Item.Properties["Modified"] = file.Item.Properties["Modified"];
		//        }

		//        // modified date
		//        if (modifiedDate != null)
		//        {

		//            file.Item.Properties["Modified"] = modifiedDate;
		//            file.Item.Properties["Created"] = file.Item.Properties["Created"];
		//        }

		//        // other file properties
		//        if (otherFileProperties != null && otherFileProperties.Count > 0)
		//        {
		//            foreach (DictionaryEntry fileProperty in otherFileProperties)
		//            {
		//                if (file.Properties.ContainsKey(fileProperty.Key) && file.Properties[fileProperty.Key] != fileProperty.Value)
		//                {
		//                    // update existing property (value changed)
		//                    file.Properties[fileProperty.Key] = fileProperty.Value;
		//                    isUpdateFileProperties = true;
		//                }
		//                else
		//                {
		//                    // add property
		//                    file.Properties.Add(fileProperty.Key, fileProperty.Value);
		//                    isUpdateFileProperties = true;
		//                }
		//            }
		//        }

		//        // other listItem properties
		//        if (otherListItemProperties != null && otherListItemProperties.Count > 0)
		//        {
		//            foreach (DictionaryEntry listItemProperty in otherListItemProperties)
		//            {
		//                if (file.Item.Properties.ContainsKey(listItemProperty.Key))
		//                {
		//                    // update property
		//                    file.Item.Properties[listItemProperty.Key] = listItemProperty.Value;
		//                }
		//                else
		//                {
		//                    // add property
		//                    file.Item.Properties.Add(listItemProperty.Key, listItemProperty.Value);
		//                }
		//            }
		//        }

		//        // save (listItem Properties)
		//        file.Item.UpdateOverwriteVersion();

		//        // save (file properties)
		//        if (isUpdateFileProperties) { file.Update(); }
		//    }
		//    catch (Exception ex)
		//    {
		//        Log.WriteError("7itkyddffrtt768g", ex);
		//        file = null;
		//    }
		//    return file;
		//}


		///// <summary>
		///// Helper object to store file properties since I can's save a new SPFile directly (can only Add)
		///// http://msdn.microsoft.com/en-us/library/microsoft.sharepoint.spfile_properties.aspx
		///// </summary>
		//public class FileProperties
		//{
		//    public string Filename { get; set; }
		//    public byte[] Binary { get; set; }
		//    public Dictionary<string, object> OtherProperties { get; set; }

		//    public Hashtable GetProperties()
		//    {
		//        Hashtable h = new Hashtable();
		//        if (!string.IsNullOrEmpty(Filename)) { h.Add("Name", Filename); }

		//        return h;
		//    }
		//}

		


		/// <summary>
		/// Update existing SPFiles in a library
		/// </summary>
		/// <param name="files"></param>
		/// <returns></returns>
		public static bool Update(List<SPFile> files)
		{
			bool isSuccess = false;
			try
			{
				List<SPFile> filesFailedToUpdate = new List<SPFile>();
				Update(files, false, ref filesFailedToUpdate);

				if (filesFailedToUpdate.Count > 0) { isSuccess = false; }
				else { isSuccess = true; }
			}
			catch (Exception ex)
			{
				Log.WriteError("t7idytHHrdred", ex);
				isSuccess = false;
			}
			return isSuccess;
		}

		/// <summary>
		/// Update existing SPFiles in a library
		/// can return a trimmed list of just those that successfully updated
		/// </summary>
		/// <param name="filesToUpdate"></param>
		/// <param name="isRemoveFilesFailedToUpdate"></param>
		/// <param name="filesFailedToUpdate"></param>
		public static void Update(List<SPFile> filesToUpdate, bool isRemoveFilesFailedToUpdate,
			ref List<SPFile> filesFailedToUpdate)
		{
			string currentFilename = null;
			try
			{
				// init
				if (filesToUpdate == null) { throw new Exception("Passed an empty collection of SPFile"); }


				if (filesToUpdate.Count > 0)
				{
					foreach (SPFile file in filesToUpdate)
					{
						currentFilename = file.Name;
						try
						{
							file.Update();
						}
						catch (Exception ex)
						{
							filesFailedToUpdate.Add(file);
							Log.WriteError("r6dfhtdrdsrt", "Failed to save item: " + currentFilename + ". Error: " + ex.Message + " \n\n\n" + ex.ToString());
						}
					}

					// remove documents that failed to save
					if (isRemoveFilesFailedToUpdate && filesFailedToUpdate != null && filesFailedToUpdate.Count > 0)
					{
						for (int i = 0; i < filesToUpdate.Count; i++)
						{
							bool isMatch = false;
							foreach (SPFile file in filesFailedToUpdate)
							{
								if (filesToUpdate[i] == file)
								{
									isMatch = true;
									break;
								}
							}
							if (isMatch)
							{
								filesToUpdate.RemoveAt(i);
								i--;
							}
						}
					} // remove items that failed to save
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("6uetrrsyese5r576dt", ex, "current file: " + currentFilename);
			}
		}

		/// <summary>
		/// SharePoint URL Illegal Chars
		/// key = illegal string
		/// value = suggested replacement string
		/// </summary>
		public static Dictionary<string, string> IllegalUrlChars
		{
			get
			{
				return new Dictionary<string, string>()
				{
					// trailing elipses
					{ "...", "-" },
					{ "..", "." },

					// punctuation marks
					{ "~", "-" },
					{ "`", "" },
					{ "!", "" },
					{ "@", "-" },
					{ "#", "" },
 					{ "$", "" },
					{ "%", "percent" }, 
					{ "^", "" },
					{ " & ", " and " }, 
					{ "&", " and " }, 
					{ "-&-", "-and-" }, 
					{ "*", "-" }, 
					{ "(", "" },
					{ ")", "" },
					{ "_", "-" },
					{ "+", "-" },
 					{ "=", "-" },
					{ "[", "" },
					{ "{", "" }, 
					{ "]", "" }, 
					{ "}", "" }, 
					{ "|", "-" }, 
					{ "\\", "-" }, 
					{ ";", "-" }, 
					{ ":", "-" }, 
					{ "'", "" }, 
					{ "\"", "" }, 
					{ ",", "" }, 
					{ "<", "-" }, 
					{ ". ", "-" }, { ".", "-" }, 
					{ ">", "-" }, 
					{ "/", "-" }, 
					{ "?", "" }, 

					// dash symbols
					{ "—", "-" }, 
					{ "–", "-" }, 
					{ "―", "-" }, 
					{ "‒", "-" },

					// quote symbols
					{ "‘", "" },
					{ "“", "" },
					{ "”", "" },
					{ "’", "" }, 

					// special marks
					{ "™", "" },
					{ "©", "" },
					{ "®", "" }, 
					{ "º", "" }, { "˚", "" }, 
					{ "\u00A0", " " }, // non breaking space
					{ "\r", "" }, 
					{ "\n", "" },
					{ "\t", "-" }, 

					// foreign chars
					{ "ä", "a" }, { "Ä", "A" },
					{ "ç", "c" }, { "Ç", "C" }, 
					{ "é", "e" }, { "É", "E" },
					{ "ê", "e" }, { "Ê", "E" }, 
					{ "ñ", "n" }, { "Ñ", "N" },
					{ "ö", "o" }, { "Ö", "O" }, 
					{ "ô", "o" }, { "Ô", "O" }, 
					{ "ü", "u" }, { "Ü", "U" }, 
					{ "€", "" },
					{ "¥", "" },
					{ "£", "" }, 
				};
			}
		}

		/// <summary>
		/// Format a filename or foldername for SharePoint
		/// removes illegal chars, limit to max 128 char, etc.,
		/// http://blogs.msdn.com/b/joelo/archive/2007/06/27/file-name-length-size-and-invalid-character-restrictions-and-recommendations.aspx
		/// </summary>
		/// <param name="filename">Must be url decoded filename.</param>
		/// <param name="numberCharAlreadyInUrl">Number of char in full path, excluding the domain name and filename</param>
		/// <param name="isConvertSpacesToDashes">Converts all spaces in filename to dashes (double dash also converted to single dash)</param>
		/// <param name="isConvertUnderscoresToDashes">Converts all underscrores to dashes (double dash also converted to single dash)</param>
		/// <param name="isUrlEncode">If true, uses HttpUtil.UrlEncode() on filename</param>
		/// <returns></returns>
		public static string FormatFilename(string filename, int numberCharAlreadyInUrl, bool isConvertSpacesToDashes,
			bool isConvertUnderscoresToDashes, bool isUrlEncode)
		{
			string originalFilename = filename;
			try
			{
				// char limit
				int extensionMaxLength = 10;	// max length for extension after the "."
				int filenameLimit = 128;	// default filename length limit
				int urlLimit = 256;		// full url path char limit, minus domain name
				filenameLimit = (urlLimit - numberCharAlreadyInUrl) < 128 ? (urlLimit - numberCharAlreadyInUrl) : filenameLimit;

				if (string.IsNullOrEmpty(filename)) { throw new Exception("Filename passed is null"); }
				else
				{
					// no trailing spaces allowed
					filename = filename.Trim();
					if (filename == ".") { throw new Exception("Filename passed is a period"); }
					if (filename.Length == 0) { throw new Exception("Filename passed is zero length after trimming"); }

					// no trailing periods allowed
					if (filename.Length > 1)
					{
						if (filename.Substring(filename.Length - 1) == ".") { filename = filename.Substring(0, filename.Length - 1); }
						if (filename.Substring(0, 1) == ".") { filename = filename.Substring(1, filename.Length - 1); }
					}

					// no double periods allowed
					filename = filename.Replace("..", ".").Replace("..", ".").Replace("..", ".");

					// separate out ext
					string ext = string.Empty;
					if (filename.Contains(".") && filename.Length > 1)
					{
						ext = filename.Substring(filename.LastIndexOf("."));
						if (!string.IsNullOrEmpty(ext) && ext.Length <= extensionMaxLength)
						{
							filename = filename.Substring(0, filename.LastIndexOf("."));
						}
						else
						{
							// either no extension, or extension too long. Don't use & leave filename alone
							ext = string.Empty;
						}
					}
					if (filename.Length == 0 && ext.Length > 0) { throw new Exception("After formatting name, only file extension remains"); }

					// remove illegal chars
					foreach (KeyValuePair<string, string> kvp in SPHelper.File.IllegalUrlChars)
					{
						filename = filename.Replace(kvp.Key, kvp.Value);
					}

					// remove double spaces
					filename = filename.Replace("  ", " ").Replace("  ", " ").Replace("  ", " ");

					// trim again
					filename = filename.Trim();

					// char limit
					if ((filename.Length + ext.Length) > filenameLimit)
					{
						string filenameTest = filename;
						filename = SPHelper.Utilities.TruncateOnWordBoundary(filename, (filenameLimit - ext.Length));
						if (string.IsNullOrEmpty(filename) || (filename.Length + ext.Length) > filenameLimit)
						{
							// force normal truncate and report error
							SPHelper.Log.WriteError("we6ijrh5sredrst", "Failed to truncate filename on word boundary, will use normal truncate instead", "filename: " + filename);
							filename = filenameTest.Substring(0, (filenameLimit - ext.Length));
						}
					}

					// trim again
					filename = filename.Trim();

					// remove trailing space, dash, or period char before the extension
					if (filename.Length > 0)
					{
						char[] trailingCharToRemove = new char[] { ' ', '-', '.' };
						while (true)
						{
							bool isCharRemoved = false;
							foreach (char c in trailingCharToRemove)
							{
								if (filename[filename.Length - 1] == c)
								{
									// remove trailing char
									filename = filename.Substring(0, filename.Length - 1);
									isCharRemoved = true;
									break;
								}
							}
							if (!isCharRemoved)
							{
								// no char removed, don't need to run this any longer
								break;
							}
							else if (filename.Length == 0) { throw new Exception("Filename is empty after removing illegal chars before ext."); }
						}
					}

					// convert spaces to dashes?
					if (isConvertSpacesToDashes)
					{
						filename = filename.Replace("  ", " ").Replace("  ", " ").Replace(" - ", "-").Replace(" ", "-")
							.Replace("--", "-").Replace("--", "-");
					}

					if (isConvertUnderscoresToDashes)
					{
						filename = filename.Replace("_", "-").Replace("--", "-").Replace("--", "-");
					}

					// put filename and ext back together
					filename += ext;

					// url encode?
					if (isUrlEncode) { filename = HttpUtility.UrlEncode(filename); }
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("rtu6r75ydtrsrdsdrg", ex, "original filename passed: " + originalFilename);
				filename = null;
			}
			return filename;
		}

		/// <summary>
		/// Format filename or foldername, remove illegal chars and truncate to max 128 char, etc.,
		/// Does not url encode the filename
		/// </summary>
		/// <param name="filename"></param>
		/// <returns></returns>
		public static string FormatFilename(string filename)
		{
			return FormatFilename(filename, 0, false, false, false);
		}



		/// <summary>
		/// get file extension
		/// </summary>
		/// <param name="item"></param>
		/// <returns></returns>
		public static string GetExtension(SPListItem item)
		{
			string ext = null;
			try
			{
				// init
				if (item == null) { throw new Exception("SPListItem passed is null"); }


				// get extension from filename
				if (item.File != null) { ext = GetExtension(item.File.Name); }
				
				// get extension from url if not from filename
				if (string.IsNullOrEmpty(ext) && item.Url.Contains("."))
				{
					ext = GetExtension(item.Url);
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("r5yyr5ddrtdrsewer", ex);
				ext = null;
			}
			return ext;
		}

		/// <summary>
		/// get extension
		/// </summary>
		/// <param name="filename"></param>
		/// <returns></returns>
		public static string GetExtension(string filename)
		{
			string ext = null;
			if (!string.IsNullOrEmpty(filename) && filename.Contains(".") && filename.Substring(filename.Length - 1) != ".")
			{
				ext = filename.Substring(filename.LastIndexOf(".") + 1);
			}
			return ext;
		}


		/// <summary>
		/// Get url for passed SPFile
		/// </summary>
		/// <param name="file"></param>
		/// <param name="isAbsoluteUrl"></param>
		/// <returns></returns>
		public static string GetUrl(SPFile file, bool isAbsoluteUrl)
		{
			// call GetUrl from Item namespace
			if (file == null) { SPHelper.Log.WriteError("erttydgfdszsddfgfgd", "SPFile passed is null"); return null; }

			if (!isAbsoluteUrl) { return file.ServerRelativeUrl; }
			else
			{
				var url = SPHelper.Web.GetUrl(file.Web.Site.RootWeb, isAbsoluteUrl);
				if (!string.IsNullOrEmpty(url) && url.Substring(url.Length - 1) == "/")
				{
					url = url.Substring(0, url.Length - 1);
				}
				url += file.ServerRelativeUrl;
				return url;
			}
		}

		/// <summary>
		/// Get relative url for passed SPFile
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public static string GetUrl(SPFile file)
		{
			return GetUrl(file, false);
		}

		/// <summary>
		/// Get icon url for passed file
		/// </summary>
		/// <param name="file"></param>
		/// <returns></returns>
		public static string GetIconUrl(SPFile file)
		{
			string o = null;
			try
			{
				// init
				if (file == null) { throw new Exception("SPFile passed is null"); }
				string iconFilename = null;


				if (file.Item != null)
				{
					// get icon url from SPListItem
					o = Item.GetIconUrl(file.Item);
				}
				else
				{
					// get icon from spfile
					if (!string.IsNullOrEmpty(file.IconUrl)) { iconFilename = file.IconUrl; }
					else
					{
						// get icon via file extension
						string ext = file.Url.Substring(file.Url.LastIndexOf(".") + 1);
						iconFilename = GetIconUrl(ext);
					}

					if (string.IsNullOrEmpty(iconFilename)) { iconFilename = "icgen.gif"; }

					// prepend relative url
					o = "/_layouts/images/" + iconFilename;
				}
			}
			catch (Exception ex)
			{
				Log.WriteError("tu6jgfhsddsrtes", ex);
				o = null;
			}
			return o;
		}



		/// <summary>
		/// Get icon url based on extension passed
		/// </summary>
		/// <param name="ext"></param>
		/// <returns></returns>
		public static string GetIconUrl(string ext)
		{
			string o = null;
			try
			{
				// init
				string iconFilename = null;
				if (!string.IsNullOrEmpty(ext)) { ext = ext.ToLower().Replace(".","").Trim(); }

				// default icon filename
				if (string.IsNullOrEmpty(ext)) { iconFilename = "icgen.gif"; }
				else
				{
					if (ext == "url" || ext == "lnk" || ext == "htm" || ext == "html") { iconFilename = "ichtm.gif"; }
					else if (ext == "ics") { iconFilename = "itevent.gif"; }
					else if (ext == "vcf") { iconFilename = "itcontct.gif"; }
					else { iconFilename = "ic" + ext + ".gif"; }
				}
				
				// prepend relative url
				o = "/_layouts/images/" + iconFilename;
			}
			catch (Exception ex)
			{
				Log.WriteError("t6ugjydfgtdstrds", ex, "ext: " + ext);
				o = null;
			}
			return o;
		}

		/// <summary>
		/// Bulk delete many files from library
		/// Will not deleted checked out files, will not delete homepage in Pages library
		/// </summary>
		/// <param name="list"></param>
		/// <param name="files"></param>
		/// <param name="isDisableRecycleBin">If true, will delete all existing items in recycle bin!</param>
		/// <returns></returns>
		public static bool BulkDeleteFiles(SPList list, SPListItemCollection files, bool isDisableRecycleBin)
		{
			bool isSuccess = false;
			try
			{
				// init
				if (list == null) { throw new Exception("SPList passed is null"); }
				if (files == null || files.Count == 0) { throw new Exception("No files passed"); }


				// call original from the item namespace
				isSuccess = Item.BulkDeleteItems(list, files, isDisableRecycleBin);
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("u5i7y9809t8ukyjdhtrgdfrsdet45r54f", ex);
				isSuccess = false;
			}
			return isSuccess;
		}

		/// <summary>
		/// Delete all files in passed library
		/// Does not delete checked out files, will not delete homepage in Pages library
		/// </summary>
		/// <param name="list"></param>
		/// <param name="isDisableRecycleBin"></param>
		/// <returns></returns>
		public static bool DeleteAllFiles(SPList list, bool isDisableRecycleBin)
		{
			// call original from Item namespace
			return Item.DeleteAllItems(list, isDisableRecycleBin);
		}

		public static Int64 GetSizeInBytes(SPFile file)
		{
			if (file != null) { return file.Length; }
			else { return 0; }
		}

		public static double GetSizeInKilobytes(SPFile file, int numberOfDecimals)
		{
			try
			{
				if (file != null) 
				{
					return Math.Round((double)(file.Length / 1024), numberOfDecimals); 
				}
				else { return 0; }
			}
			catch (Exception ex)
			{
				string filename = file != null ? file.Name : null;
				SPHelper.Log.WriteError("re6j7itydfdrsd", ex, "filename: " + filename);
				return 0;
			}
		}

		public static double GetSizeInMegabytes(SPFile file, int numberOfDecimals)
		{
			try
			{
				if (file != null)
				{
					return Math.Round((double)(file.Length / 1048576), numberOfDecimals);
				}
				else { return 0; }
			}
			catch (Exception ex)
			{
				string filename = file != null ? file.Name : null;
				SPHelper.Log.WriteError("re6j7itydfdrsd", ex, "filename: " + filename);
				return 0;
			}
		}

		public static double GetSizeInGigabytes(SPFile file, int numberOfDecimals)
		{
			try
			{
				if (file != null)
				{
					return Math.Round((double)(file.Length / 1073741824), numberOfDecimals);
				}
				else { return 0; }
			}
			catch (Exception ex)
			{
				string filename = file != null ? file.Name : null;
				SPHelper.Log.WriteError("r6ujytdhfdfdrgt", ex, "filename: " + filename);
				return 0;
			}
		}

		public static string GetSize(SPFile file, int numberOfDecimals)
		{
			string size = "0 KB";
			if (file != null)
			{
				var sizeInBytes = GetSizeInBytes(file);
				var sizeInKiloBytes = GetSizeInKilobytes(file, numberOfDecimals);
				var sizeInMegabytes = GetSizeInMegabytes(file, numberOfDecimals);
				var sizeInGigabytes = GetSizeInGigabytes(file, numberOfDecimals);

				if (Math.Round(sizeInGigabytes, 0) > 0)
				{
					// greater than 1 gigabyte
					size = sizeInGigabytes + " GB";
				}
				else if (Math.Round(sizeInMegabytes, 0) > 0)
				{
					// greater than 1 megabyte
					size = sizeInMegabytes + " MB";
				}
				else if (Math.Round(sizeInKiloBytes, 0) > 0)
				{
					// greater than 1 kilobyte
					size = sizeInKiloBytes + " KB";
				}
				else
				{
					// show bytes
					size = sizeInBytes + " B";
				}
			}
			return size;
		}

		/// <summary>
		/// Send SPFile to user's browser
		/// </summary>
		/// <param name="file"></param>
		/// <param name="mimeType">if null, will use "application/force-download" mime type</param>
		public static void SendFileToBrowser(SPFile file, string mimeType)
		{
			if (file != null && HttpContext.Current != null && HttpContext.Current.Response != null)
			{
				// default mime type, force download
				if (string.IsNullOrEmpty(mimeType)) { mimeType = GetMimeType(file.Name); }

				HttpContext.Current.Response.ClearHeaders();
				HttpContext.Current.Response.ClearContent();
				HttpContext.Current.Response.ContentType = mimeType;
				HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=\"" + file.Name + "\"");
				HttpContext.Current.Response.BinaryWrite(file.OpenBinary());
				HttpContext.Current.Response.End();
			}
			else
			{
				SPHelper.Log.WriteError("65t89drtxdfdxfsdzrserre", "No SPFile passed to send to browser");
			}
		}
		/// <summary>
		/// Send SPListItem's attached SPFile to user's browser
		/// </summary>
		/// <param name="item"></param>
		/// <param name="mimeType">if null, will use "application/force-download" mime type</param>
		public static void SendFileToBrowser(SPListItem item, string mimeType)
		{
			if (item != null) { SendFileToBrowser(item.File, mimeType); }
		}

		/// <summary>
		/// get mime type based on file extension
		/// </summary>
		/// <param name="filename"></param>
		/// <returns></returns>
		public static string GetMimeType(string filename)
		{
			string mimeType = "application/force-download";
			try
			{
				if (!string.IsNullOrEmpty(filename))
				{
					string ext = GetExtension(filename);
					if (!string.IsNullOrEmpty(ext))
					{
						ext = ext.ToLower();
						if		(ext == "pdf")	{ mimeType = "application/pdf"; }
						else if (ext == "zip")	{ mimeType = "application/zip"; }
						else if (ext == "doc")	{ mimeType = "application/msword"; }
						else if (ext == "docx")	{ mimeType = "application/vnd.openxmlformats-officedocument.wordprocessingml.document"; }
						else if (ext == "xls")	{ mimeType = "application/vnd.ms-excel"; }
						else if (ext == "xlsx")	{ mimeType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"; }
						else if (ext == "ppt")	{ mimeType = "application/vnd.ms-powerpoint"; }
						else if (ext == "pptx")	{ mimeType = "application/vnd.openxmlformats-officedocument.presentationml.presentation"; }
						else if (ext == "txt")	{ mimeType = "text/plain"; }
						else { SPHelper.Log.WriteError("65rdydtsddsfsz", "Failed to find mime format for extension: " + ext); }
					}
				}
			}
			catch (Exception ex)
			{
				SPHelper.Log.WriteError("e65uthdsfdrdzssee5s6r6f", ex, "filename: " + filename);
			}
			return mimeType;
		}






	} // file class
}
